#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include "LockGuard.hpp"
#include "Log.hpp"
#include "Thread.hpp"
#include <functional>

namespace ThreadNs
{
    class ThreadData
    {
    public:
        ThreadData(const std::string &name) : threadname(name)
        {
        }

    public:
        std::string threadname;
    };

    const int defaultNum = 3;
    template <class T>
    class ThreadPool
    {
    public:
        ThreadPool(int thread_num = defaultNum)
            : _thread_num(thread_num)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);
            // 创建_thread_num个线程
            for (int i = 0; i < _thread_num; i++)
            {
                std::string threadname = "thread-";
                threadname += std::to_string(i + 1);
                ThreadData td(threadname);
                _threads.emplace_back(threadname,
                                      std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1),
                                      td);
                // 打印创建线程名字的日志
                lg.LogMessage(Info, "%s is create...\n", threadname.c_str());
            }
        }

        static ThreadPool<T> *GetInstance()
        {
            if (instance == nullptr)
            {
                LockGuard lockguard(&_sig_lock);
                if (instance == nullptr)
                {
                    lg.LogMessage(Info, "创建单例模式\n");
                    instance = new ThreadPool<T>();
                }
            }
            return instance;
        }

        ThreadPool(const ThreadPool<T> &td) = delete;               // 不能使用拷贝构造函数
        ThreadPool<T> &operator=(const ThreadPool<T> &td) = delete; //  不能使用赋值运算符重载

        void Start()
        {
            for (auto &thread : _threads)
            {
                thread.Start();
                lg.LogMessage(Info, "%s is running...\n", thread.GetThreadName().c_str());
            }
        }

        void ThreadRun(ThreadData &td)
        {
            while (true)
            {
                T t;
                // 取任务
                {
                    LockGuard lockguard(&_mutex);
                    while (_q.empty())
                    {
                        ThreadWait(td);
                        lg.LogMessage(Debug, "thread is wakeup\n", td.threadname.c_str());
                    }
                    t = _q.front();
                    _q.pop();
                }

                // 处理任务
                t();
                // lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
                //               td.threadname.c_str(),
                //               t.PrintTask().c_str(), t.Printresult().c_str());
            }
        }

        // 在线程池中放入任务
        void Push(T &in)
        {
            // lg.LogMessage(Debug, "other thread push a task, task is %s\n", in.PrintTask().c_str());
            LockGuard lockguard(&_mutex);
            _q.push(in);
            ThreadWakeup(); // 唤醒一个线程处理任务
        }

        void ThreadWakeup()
        {
            pthread_cond_signal(&_cond);
        }

        void ThreadWait(const ThreadData &td)
        {
            lg.LogMessage(Debug, "no task, %s is a sleeping...\n", td.threadname.c_str());
            pthread_cond_wait(&_cond, &_mutex);
        }

        void Wait()
        {
            for (auto &thread : _threads)
            {
                thread.Join();
            }
        }

        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

    public:
        std::queue<T> _q;
        std::vector<Thread<ThreadData>> _threads;
        int _thread_num;
        pthread_mutex_t _mutex;         // 锁
        pthread_cond_t _cond;           // 信号量
        static ThreadPool<T> *instance; // 单例模式

        static pthread_mutex_t _sig_lock; // 单例锁
    };

    template <class T>
    ThreadPool<T> *ThreadPool<T>::instance = nullptr;

    template <class T>
    pthread_mutex_t ThreadPool<T>::_sig_lock = PTHREAD_MUTEX_INITIALIZER;
}